package com.xiang.publicdemo.module1.repository.impl;

import com.xiang.publicdemo.common.utils.StringUtils;
import com.xiang.publicdemo.module1.entity.AbstractEntity;
import com.xiang.publicdemo.module1.repository.IAbstractRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import javax.persistence.Column;
import javax.persistence.Table;
import java.beans.Transient;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

@Repository
@Slf4j
public abstract class AbstractRepositoryImpl<E extends AbstractEntity> implements IAbstractRepository<E> {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
    private Class<E> beanClass;

    private String insertSql;
    private String findAllSql;
    private String findOneByWhereSql;
    private String deleteAllSql;
    private String tableName;
    /**
     * 表格中的字段
     */
    private List<String> columnNames = new ArrayList<>(64);
    /**
     * beanClass属性列表
     */
    private List<Field> fields = new ArrayList<>();

    /**
     * 其他bean的class。在BeanPropertySqlParameterSource中注册相应的类型,
     * key为字段类型，value为字段类型对应的id类型.
     */
    private Map<String, Class<?>> otherBeanClasses = new HashMap<>();

    public AbstractRepositoryImpl() {

        Type genericSuperclass = this.getClass().getGenericSuperclass();
        if (ParameterizedType.class.isAssignableFrom(genericSuperclass.getClass())) {
            ParameterizedType ps = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = ps.getActualTypeArguments();
            beanClass = (Class<E>) actualTypeArguments[0];
            Table table = beanClass.getAnnotation(Table.class);
            if (table != null && !StringUtils.isEmpty(table.name())) {
                //如果有@Table注解，并且有自定义名称，则已自定义名称为tableName，否则直接使用类名做tablename
                tableName = table.name();
            }
            if (StringUtils.isBlank(tableName)) {
                tableName = ClassUtils.getShortName(beanClass);
            }
            tableName = tableName.toLowerCase();
        }

        Assert.notNull(beanClass, "entityClass cannot be null.");
        // 设置数据库字段与class的field对应关系
        ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                String name = field.getName();
                // 如果是数组或者集合或者接口，就不是对应的数据库字段
                Class<?> type = field.getType();
                if (Collection.class.isAssignableFrom(type) || type.isArray()
                        || (type.isInterface() && !Serializable.class.equals(type))) {
                    return;
                }
                fields.add(field);
                Column col = field.getAnnotation(Column.class);
                Transient tran = field.getAnnotation(Transient.class);
                if (tran == null && !Modifier.isStatic(field.getModifiers())) {
                    //只处理没有被@Transient注解，并且不是静态属性
                    if (null != col && StringUtils.isNotBlank(col.name())) {
                        //如果有指定@Column name 则以注解为准，否则以属性名为数据库字段
                        columnNames.add(col.name());
                    } else {
                        columnNames.add(name);
                    }
                }
            }
        });

        Assert.notEmpty(columnNames, "cannot find any columns of beans " + beanClass);
        // 生成字段名和字段值
        StringBuilder cs = new StringBuilder();
        StringBuilder vs = new StringBuilder();
        for (String columnName : columnNames) {
            // 自增列不需要添加
            if ("id".equals(columnName)) {
                continue;
            }
            if (cs.length() > 0) {
                cs.append(",");
            }
            if (vs.length() > 0) {
                vs.append(",");
            }
            cs.append(columnName);
            vs.append(":");
            vs.append(columnName);
        }

        // 生成insert语句
        StringBuilder insert = new StringBuilder(32);
        insert.append("insert into ");
        insert.append(tableName);
        insert.append("(");
        insert.append(cs);
        insert.append(") values(");
        insert.append(vs);
        insert.append(")");

        insertSql = insert.toString();

        // 查询所有的语句
        findAllSql = "select * from " + tableName;
        // 删除所有的语句
        deleteAllSql = "delete from " + tableName;
        // 自定义条件查询
        findOneByWhereSql = "select * from " + tableName + " where ";
    }

    @Override
    public E add(E e) {
        log.debug("add entity:{}", e.toString());
        int resultCNT = 0;
        MyBeanPropertySqlParameterSource beanPropertySqlParameterSource = new MyBeanPropertySqlParameterSource(e, otherBeanClasses);
        final GeneratedKeyHolder holder = new GeneratedKeyHolder();
        resultCNT = namedParameterJdbcTemplate.update(insertSql, beanPropertySqlParameterSource, holder);
        Field id = ReflectionUtils.findField(beanClass, "id");
        id.setAccessible(true);
        try {
            id.set(e, holder.getKey());
        } catch (InvalidDataAccessApiUsageException | DataRetrievalFailureException | IllegalArgumentException
                | IllegalAccessException ex) {
            log.warn("保存数据后设置id出错,原因:{}", ex.getLocalizedMessage());
        }
        id.setAccessible(false);
        return e;
    }

    @Override
    public int delete(String id) {
        return 0;
    }

    @Override
    public E update(E e) {
        return null;
    }

    @Override
    public E findById(String id) {
        return null;
    }

    @Override
    public E findOne(E e) {
        return null;
    }

    @Override
    public List<E> find(E e) {
        return null;
    }
}
